package com.dongdongshop.service.impl;

import com.dongdongshop.dao.SeckillGoodsRepostory;
import com.dongdongshop.dao.SeckillOrderRepostory;
import com.dongdongshop.pojo.seckill.TbSeckillGoods;
import com.dongdongshop.pojo.seckill.TbSeckillOrder;
import com.dongdongshop.service.seckill.SeckillService;
import com.dongdongshop.util.IdWorker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;

/**
 * @Author Eayon
 * @Date 2020/4/5 15:03
 */
@Service
public class SeckillServiceImpl implements SeckillService {

    @Autowired
    private SeckillGoodsRepostory seckillGoodsRepostory;

    @Autowired
    private SeckillOrderRepostory seckillOrderRepostory;

    @Autowired
    private RedisTemplate redisTemplate;

    @Resource
    private IdWorker idWorker;

    /**
     * 查找所有状态正常的秒杀商品
     * @return
     */
    @Override
    public List<TbSeckillGoods> findSeckillGoodsAll() {
        //先从缓存中获取秒杀商品缓存数据
        List<TbSeckillGoods> seckillGoodsList = redisTemplate.boundHashOps("seckillGoods").values();
        if(seckillGoodsList == null || seckillGoodsList.size() <= 0){
            //如果查出来的为空说明缓存中没有，那么就从数据库中查询出来
            //查询审核状态为1，商品秒杀开始时间小于等于当前系统时间的，结束时间大于等于当前系统时间的。
            //并且库存大于0的秒杀商品信息
            seckillGoodsList = seckillGoodsRepostory.findAllByStatusEqualsAndStartTimeLessThanEqualAndEndTimeGreaterThanEqualAndStockCountGreaterThan("1", new Date(), new Date(), 0);
            //存入缓存
            for (TbSeckillGoods tbSeckillGoods : seckillGoodsList) {
                //秒杀商品
                redisTemplate.boundHashOps("seckillGoods").put(tbSeckillGoods.getId(),tbSeckillGoods);
                //该秒杀商品的剩余库存
                Integer stockCount = tbSeckillGoods.getStockCount();
                Long l = stockCount.longValue();
                redisTemplate.boundHashOps("seckillStockCount").put(tbSeckillGoods.getId(),l);
            }
        }
        //如果不为空就直接返回
        return seckillGoodsList;
    }

    /**
     * 根据秒杀商品id查询秒杀商品信息(从缓存中查询)
     * @param id
     * @return
     */
    @Override
    public TbSeckillGoods getSeckillById(Long id) {
        return (TbSeckillGoods)redisTemplate.boundHashOps("seckillGoods").get(id);
    }

    /**
     * 创建秒杀订单
     * @param seckillId
     * @param userId
     */
    @Override
    public void createSeckillOrder(Long seckillId, Long userId) {
        //从redis中获取该秒杀商品信息
        TbSeckillGoods seckillGoods = (TbSeckillGoods)redisTemplate.boundHashOps("seckillGoods").get(seckillId);
        //取出该商品的剩余库存
        Object stockCount = redisTemplate.boundHashOps("seckillStockCount").get(seckillId);
        int seckillStockCount = Integer.parseInt(stockCount.toString());

        //如果商品不存在就抛一个异常，然后调用者处理一下就好了
        if(seckillGoods==null){
            throw new RuntimeException("商品不存在");
        }
        //如果商品存在但没有库存也抛异常
        if(seckillStockCount <= 0){
            throw new RuntimeException("商品库存不足");
        }
        //判断该用户是否以及购买过该秒杀商品
        Long seckillGoodsOnlyOne = (Long)redisTemplate.boundValueOps("seckillGoodsOnlyOne" + userId + seckillId).get();
        if(seckillGoodsOnlyOne == null){
            //限制他下次不能购买
            redisTemplate.boundValueOps("seckillGoodsOnlyOne" + userId + seckillId).set(seckillId);
        }else {
            throw new RuntimeException("你已秒杀过该商品");
        }
        //通过redis的原子行操作对该商品的剩余库存进行扣除库存操作，并返回扣减库存后的剩余库存
        //Long increment = redisTemplate.boundHashOps("seckillStockCount").increment(seckillId,-1);
        //上面的原子口库存我用会报错，所以就直接减了
        seckillGoods.setStockCount(seckillGoods.getStockCount()-1);
        //再重新放回缓存
        redisTemplate.boundHashOps("seckillGoods").put(seckillId,seckillGoods);

        //防止上一步回同时有多个线程进行操作，所以在这里再进行一次判断，过滤掉并发时多线程超卖情况
        TbSeckillGoods seckillGoods2 = (TbSeckillGoods)redisTemplate.boundHashOps("seckillGoods").get(seckillId);
        Integer increment = seckillGoods2.getStockCount();
        if(increment < 0){
            throw new RuntimeException("商品库存不足");
        }
        //再次判断扣减库存后，是否还有剩余库存
        if(increment == 0){  //如果正好卖光了
            //该该商品的库存设置为0
            seckillGoods.setStockCount(0);
            //将数据同步到数据库中，只有当缓存中剩余库存为0才同步数据库，减少压力
            seckillGoodsRepostory.save(seckillGoods);
            //并且删除缓存中该秒杀商品
            redisTemplate.boundHashOps("seckillGoods").delete(seckillId);
        }
        //生成订单，因为我懒所以这里就不发送mq给秒杀订单服务去操作了
        //因为扣减库存并不是操作的redis中秒杀对象，而是redis中的剩余库存对象，所以给秒杀对象重新设置扣减过后的库存
        //将秒杀订单保存在redis中
        TbSeckillOrder seckillOrder = new TbSeckillOrder();
        seckillOrder.setId(idWorker.nextId());  //订单ID
        seckillOrder.setCreateTime(new Date()); //创建时间
        seckillOrder.setMoney(seckillGoods.getCostPrice()); //秒杀价格
        seckillOrder.setSeckillId(seckillId);   //秒杀商品ID
        seckillOrder.setSellerId(seckillGoods.getSellerId());   //买家ID
        seckillOrder.setUserId(userId.toString()); //买家ID
        seckillOrder.setStatus("0");    //订单状态待付款
        //保存到redis中
        redisTemplate.boundHashOps("seckillOrder").put(userId,seckillOrder);
    }
}
